package com.fxz.oauth.security.filter;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fxz.oauth.config.AppProperties;
import com.fxz.oauth.util.CollectionUtil;
import com.fxz.oauth.util.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 自定义的JWT过滤器
 *
 * @author Fxz
 * @version 1.0
 * @date 2021-08-04 15:02
 */
@RequiredArgsConstructor
@Component
public class JwtFilter extends OncePerRequestFilter {

    private final AppProperties appProperties;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        //检查HTTP请求头是否携带token
        if (checkJwtToken(httpServletRequest)) {
            //从jwt中解析出Claims
            Claims claims = validateToken(httpServletRequest);
            //如果Claims是空 或者Claims中不存在authorities
            if (claims == null || claims.get("authorities") == null) {
                //为空 认证失败 清空SecurityContext
                SecurityContextHolder.clearContext();
            } else {
                //不为空 获取authorities
                List<?> rawList = CollectionUtil.convertObjectToList(claims.get("authorities"));
                //将list转变为固定结构的list
                List<SimpleGrantedAuthority> collect = rawList.stream()
                        .map(String::valueOf)
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());
                //创建一个UsernamePasswordAuthenticationToken
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(claims.getSubject(), null, collect);
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    /**
     * 在请求头中获取访问token 并且解析出Claims
     * @param request
     * @return
     */
    private Claims validateToken(HttpServletRequest request) {
        String jwtToken = request.getHeader(appProperties.getJwt().getHeader()).replace(appProperties.getJwt().getPrefix(), "");
        try {
            return Jwts.parserBuilder().setSigningKey(JwtUtil.key).build().parseClaimsJws(jwtToken).getBody();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 检查jwt token是否在http报头中
     *
     * @param request HTTP请求
     * @return 是否有JWT TOKEN
     */
    private boolean checkJwtToken(HttpServletRequest request) {
        String header = request.getHeader(appProperties.getJwt().getHeader());
        return StringUtils.isNotEmpty(header) && header.startsWith(appProperties.getJwt().getPrefix());
    }
}
